﻿//-----------------------------------------------------------------------
// <copyright file="WebHelpers.cs" company="AFS">
// Copyright (c) AFS. All rights reserved.
//
//    This file is part of Chatbox.
//
//    Chatbox is free software: you can redistribute it and/or modify
//    it under the terms of the GNU Lesser General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    any later version.
//
//    Chatbox is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//    GNU Lesser General Public License for more details.
//
//    You should have received a copy of the GNU Lesser General Public License
//    along with Chatbox. If not, see http://www.gnu.org/copyleft/lesser.html
// </copyright>
//----------------------------------------------------------------------- 

namespace ChatBox.Utility
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Net;
    using System.Text;
    using System.Web;

    /// <summary>
    /// Helper methods for web requests
    /// </summary>
    public static class WebHelpers
    {
        /// <summary>
        /// Above this, chars are not normal ascii
        /// </summary>
        private const int MaxNormalChar = 127;

        /// <summary>
        /// Post the data as a multipart form
        /// </summary>
        /// <param name="postUri">the user agent to post to</param>
        /// <param name="userAgent">the url to send to</param>
        /// <param name="values">the key and value pairs to send</param>
        /// <param name="cookies">the cookies to send</param>
        /// <returns>the http response from the operation</returns>
        public static HttpWebResponse MultipartFormDataPost(Uri postUri, string userAgent, Dictionary<string, string> values, IEnumerable<Cookie> cookies)
        {
            string formDataBoundary = "---------------------------" + WebHelpers.RandomHexDigits(12);
            string contentType = "multipart/form-data; charset=UTF-8; boundary=" + formDataBoundary;

            string formData = WebHelpers.MakeMultipartForm(values, formDataBoundary);
            return WebHelpers.PostForm(postUri, userAgent, contentType, formData, cookies);
        }

        /// <summary>
        /// Post a form
        /// </summary>
        /// <param name="postUri">the url to post to</param>
        /// <param name="userAgent">the user agent string to send</param>
        /// <param name="contentType">the form content type</param>
        /// <param name="formData">the text of the form data to send</param>
        /// <param name="cookies">the cookies to send</param>
        /// <returns>the http reposnse from the operation</returns>
        public static HttpWebResponse PostForm(Uri postUri, string userAgent, string contentType, string formData, IEnumerable<Cookie> cookies)
        {
            HttpWebRequest request = WebRequest.Create(postUri) as HttpWebRequest;

            if (request == null)
            {
                throw new ArgumentException("request is not a http request");
            }

            // Add these, as we're doing a POST
            request.Method = "POST";
            request.ContentType = contentType;
            request.UserAgent = userAgent;
            request.CookieContainer = new CookieContainer();
            if (cookies != null)
            {
                foreach (Cookie cookie in cookies)
                {
                    request.CookieContainer.Add(cookie);                    
                }
            }

            Encoding contentEncoding = Encoding.GetEncoding("ISO-8859-1");

            // I was using "Encoding.UTF8", but that doen't work. E2 insists on ISO-8859-1 (latin 1) encoded posts. 
            // And if your char can't be represented in that encoding, you must use a html char entity. Or give up
            byte[] postBytes = contentEncoding.GetBytes(formData);

            // We need to count how many bytes we're sending. 
            request.ContentLength = postBytes.Length;

            using (Stream requestStream = request.GetRequestStream())
            {
                // Push it out there
                requestStream.Write(postBytes, 0, postBytes.Length);
                requestStream.Close();
            }

            return request.GetResponse() as HttpWebResponse;
        }

        /// <summary>
        /// Generate random hex digits 
        /// </summary>
        /// <param name="count">the number of digits to generate</param>
        /// <returns>the resulting string</returns>
        public static string RandomHexDigits(int count)
        {
            Random random = new Random();
            StringBuilder result = new StringBuilder();
            for (int i = 0; i < count; i++)
            {
                int digit = random.Next(16);
                result.AppendFormat("{0:x}", digit);
            }

            return result.ToString();
        }

        /// <summary>
        /// Encode as html with high chars
        /// </summary>
        /// <param name="value">the string input</param>
        /// <returns>the encoded result</returns>
        /// <see cref="http://stackoverflow.com/questions/547634/httputility-htmlencode-doesnt-encode-everything"/>
        public static string HtmlEncodeWithHighChars(this string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return value;
            }

            // call the normal HtmlEncode first
            string normalEncoded = HttpUtility.HtmlEncode(value);

            if (!ContainsHighChar(normalEncoded))
            {
                return normalEncoded;
            }

            // convert all high chars
            StringBuilder encodedValue = new StringBuilder();
            foreach (char c in normalEncoded)
            {
                if (c > MaxNormalChar)
                {
                    // above normal ASCII
                    encodedValue.Append("&#" + (int)c + ";");
                }
                else
                {
                    encodedValue.Append(c);
                }
            }

            return encodedValue.ToString();
        }

        /// <summary>
        /// Does the string contain a high char?
        /// </summary>
        /// <param name="value">the string to test</param>
        /// <returns>true if a high char is present</returns>
        private static bool ContainsHighChar(string value)
        {
            foreach (char c in value)
            {
                if (c > MaxNormalChar)
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Turn the key and value pairs into a multipart form
        /// </summary>
        /// <param name="values">the key and value pairs</param>
        /// <param name="boundary">the item seperator string</param>
        /// <returns>the ket/value pairs formatted for a multipart form data</returns>
        private static string MakeMultipartForm(Dictionary<string, string> values, string boundary)
        {
            StringBuilder sb = new StringBuilder();

            foreach (var pair in values)
            {
                sb.AppendFormat("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}\r\n", boundary, pair.Key, pair.Value);
            }

            sb.AppendFormat("--{0}--\r\n", boundary);

            return sb.ToString();    
        }
    }
}
